package com.zhousb.deviceManager.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.db.sql.ConditionGroup;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.zhousb.deviceManager.exception.CustomerException;
import com.zhousb.deviceManager.mapper.ImOrderDetailMapper;
import com.zhousb.deviceManager.mapper.ImOrderMapper;
import com.zhousb.deviceManager.mapper.ImProductLogMapper;
import com.zhousb.deviceManager.mapper.ImProductMapper;
import com.zhousb.deviceManager.model.bo.ExportCheckedBo;
import com.zhousb.deviceManager.model.bo.OrderBO;
import com.zhousb.deviceManager.model.dto.ExportCreateDto;
import com.zhousb.deviceManager.model.dto.ExportOrderLogDto;
import com.zhousb.deviceManager.model.dto.ProductDto;
import com.zhousb.deviceManager.model.entity.*;
import com.zhousb.deviceManager.model.enums.NoticeTypeEnum;
import com.zhousb.deviceManager.model.enums.OrderStatusEnum;
import com.zhousb.deviceManager.model.enums.ProductLogOperationEnum;
import com.zhousb.deviceManager.model.so.OrderSo;
import com.zhousb.deviceManager.model.vo.OrderLogVo;
import com.zhousb.deviceManager.model.vo.OrderVo;
import com.zhousb.deviceManager.model.vo.ProductSimpleVo;
import com.zhousb.deviceManager.model.vo.RoleVo;
import com.zhousb.deviceManager.security.JwtUser;
import com.zhousb.deviceManager.service.NoticeService;
import com.zhousb.deviceManager.service.OrderService;
import com.zhousb.deviceManager.service.ProductService;
import com.zhousb.deviceManager.service.SysUserService;
import com.zhousb.deviceManager.utils.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.zhousb.deviceManager.common.Constants.*;
import static com.zhousb.deviceManager.model.enums.ProductLogOperationEnum.*;
import static com.zhousb.deviceManager.model.enums.RoleEnum.STORE_MANAGER;

@Service
public class OrderServiceImpl implements OrderService {

    @Resource
    private ImOrderMapper orderMapper;
    @Resource
    private ImOrderDetailMapper orderDetailMapper;
    @Resource
    private ImProductMapper productMapper;
    @Resource
    private ProductService productService;
    @Resource
    private NoticeService noticeService;
    @Resource
    private SysUserService sysUserService;
    @Resource
    private RedisUtil redisUtil;


    @Override
    public IPage page(Page page, OrderSo so) {
        Set<SysRole> roles = authUtils.getUser().getRoles();
        boolean isManager = false;
        for (SysRole role : roles) {
            if (role.getRoleCode().equals("STORE_MANAGER")) {
                isManager = true;
                break;
            }
        }
        List<Long> targetOrderIds = new ArrayList<>();
        List<String> keywords = new ArrayList<>();
        if (StringUtils.hasText(so.getProductName())) {
            keywords = Arrays.asList(so.getProductName().split(" "));
            targetOrderIds = orderDetailMapper.listTargetOrderId(so, so.getOrderUserId(), isManager, keywords);
            if (targetOrderIds.isEmpty()) {
                return page;
            }
        }
        IPage<OrderVo> data = orderMapper.page(page, so, so.getOrderUserId(), isManager, targetOrderIds);
        data.getRecords().forEach(orderVo -> {
            orderVo.setProducts(productMapper.listProductSimpleData(orderVo.getId()));
        });
        return data;
    }

    @Resource
    AuthUtils authUtils;

    static final Map<String, String> defaultMap = new HashMap<>();

    static {
        defaultMap.put("senderName", "刘昊然");
        defaultMap.put("senderTel", "15888888888");
        defaultMap.put("senderAddress", "浙江省嘉兴市秀洲区阳光小区");
    }

    @Override
    public boolean create(OrderBO bo) {
        JwtUser jwtUser = authUtils.getUser();
        List<ProductDto> products = bo.getProducts();
        Long orderId = IdGenerator.nextId();
        String productList = products.stream().sorted(Comparator.comparing(ProductDto::getProductId)).map(s -> s.getProductId() + "&" + s.getQuantity()).collect(Collectors.joining(","));
        //保存地址、产品的md5值到redis
        String encryptString = String.format("%s|%s", bo.getConsigneeAddress(), productList);
        String md5 = MD5.create().digestHex(encryptString);
        if (!bo.isForce()) {
            Map<Object, Object> existMap = redisUtil.hmget(TEMP_ORDER + md5);
            if (!existMap.isEmpty()) {
                throw new CustomerException("订单重复");
            }
        }
        ImOrder order = new ImOrder();

        order.setId(orderId);
        String orderCode = LocalDateTimeUtil.format(LocalDateTime.now(), "yyyyMMddHHmmssSSSS");
        order.setOrderCode(orderCode);
        order.setOrderUserId(jwtUser.getUserId());
        order.setSenderName(StringUtils.hasText(bo.getSenderName()) ? bo.getSenderName() : defaultMap.get("senderName"));
        order.setSenderTel(StringUtils.hasText(bo.getSenderTel()) ? bo.getSenderTel() : defaultMap.get("senderTel"));
        order.setSenderAddress(StringUtils.hasText(bo.getSenderAddress()) ? bo.getSenderAddress() : defaultMap.get("senderAddress"));
        order.setConsigneeName(bo.getConsigneeName());
        order.setConsigneeTel(bo.getConsigneeTel());
        order.setConsigneeAddress(bo.getConsigneeAddress());
        order.setRemark(bo.getRemark());
        order.setOrderStatus(bo.isSaveFlag() ? OrderStatusEnum.SAVED.getCode() : OrderStatusEnum.CREATED.getCode());
        order.setAfloatStatus(bo.getAfloatStatus());
        orderMapper.insert(order);

        try {
            products.forEach(productDto -> {
                updateStoreNum(orderId, Long.parseLong(productDto.getProductId()), productDto.getQuantity(), ProductLogOperationEnum.CREATE_ORDER);
            });
        } catch (Exception e) {
            orderMapper.deleteById(orderId);
            throw new CustomerException("库存更新失败，订单生成失败，请重新下单");
        }
        orderDetailMapper.batchInsert(orderId, products);

        if (!bo.isSaveFlag()) {
            String content = String.format("【%s】提交了订单【%s】，请尽快安排发货", jwtUser.getRealName(), orderCode);
            noticeService.createNotice(NoticeTypeEnum.ORDER_MESSAGE, content, orderId, sysUserService.getRoleUserMap(STORE_MANAGER.getCode()));
        }
        redisUtil.hset(TEMP_ORDER + md5, orderId.toString(), 1, HOUR_24);
        return true;
    }

    @Override
    public boolean update(OrderBO bo) {
        ImOrder imOrder = orderMapper.selectById(bo.getId());
        if (!(imOrder.getOrderStatus().equals(OrderStatusEnum.SAVED.getCode()) || imOrder.getOrderStatus().equals(OrderStatusEnum.CREATED.getCode()))) {
            throw new CustomerException("当前订单已不可编辑");
        }

        List<ProductDto> products = bo.getProducts();
        String newProductList = products.stream().sorted(Comparator.comparing(ProductDto::getProductId)).map(s -> s.getProductId() + "&" + s.getQuantity()).collect(Collectors.joining(","));
        //保存地址、产品的md5值到redis
        String newEncryptString = String.format("%s|%s", bo.getConsigneeAddress(), newProductList);
        String newMd5 = MD5.create().digestHex(newEncryptString);
        if (!bo.isForce()) {

            Map<Object, Object> existMap = redisUtil.hmget(TEMP_ORDER + newMd5);
            if (!existMap.isEmpty()) {
                boolean exist = true;
                for (Object value : existMap.keySet()) {
                    if (value.toString().equals(bo.getId())) {
                        exist = false;
                        break;
                    }
                }
                if (exist) {
                    throw new CustomerException("订单重复");
                }
            }

        }

        JwtUser jwtUser = authUtils.getUser();
        ImOrder order = orderMapper.selectById(bo.getId());
        order.setSenderName(StringUtils.hasText(bo.getSenderName()) ? bo.getSenderName() : defaultMap.get("senderName"));
        order.setSenderTel(StringUtils.hasText(bo.getSenderTel()) ? bo.getSenderTel() : defaultMap.get("senderTel"));
        order.setSenderAddress(StringUtils.hasText(bo.getSenderAddress()) ? bo.getSenderAddress() : defaultMap.get("senderAddress"));
        order.setConsigneeName(bo.getConsigneeName());
        order.setConsigneeTel(bo.getConsigneeTel());
        order.setConsigneeAddress(bo.getConsigneeAddress());
        order.setRemark(bo.getRemark());
        order.setOrderStatus(bo.isSaveFlag() ? OrderStatusEnum.SAVED.getCode() : OrderStatusEnum.CREATED.getCode());
        order.setAfloatStatus(bo.getAfloatStatus());
        orderMapper.updateById(order);

        LambdaQueryWrapper<ImOrderDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ImOrderDetail::getOrderId, order.getId());
        List<ImOrderDetail> orderDetails = orderDetailMapper.selectList(queryWrapper);

        orderDetails.forEach(detail -> {
            updateStoreNum(order.getId(), detail.getProductId(), detail.getQuantity(), BEFORE_EDIT_ORDER);
        });
        String productList = orderDetails.stream().sorted(Comparator.comparing(ImOrderDetail::getProductId)).map(s -> s.getProductId() + "&" + s.getQuantity()).collect(Collectors.joining(","));
        String encryptString = String.format("%s|%s", order.getConsigneeAddress(), productList);
        String md5 = MD5.create().digestHex(encryptString);
        redisUtil.hdel(TEMP_ORDER + md5, bo.getId());

        orderDetailMapper.delete(queryWrapper);
        products.forEach(productDto -> {
            updateStoreNum(order.getId(), Long.parseLong(productDto.getProductId()), productDto.getQuantity(), AFTER_EDIT_ORDER);
        });
        orderDetailMapper.batchInsert(order.getId(), products);

        if (!bo.isSaveFlag()) {
            String content = String.format("【%s】提交了订单【%s】，请尽快安排发货", jwtUser.getRealName(), order.getOrderCode());
            noticeService.createNotice(NoticeTypeEnum.ORDER_MESSAGE, content, order.getId(), sysUserService.getRoleUserMap(STORE_MANAGER.getCode()));
        }

        redisUtil.hset(TEMP_ORDER + newMd5, bo.getId(), 1, HOUR_24);
        return true;
    }

    @Override
    public boolean cancel(String orderId) {
        JwtUser jwtUser = authUtils.getUser();
        ImOrder order = orderMapper.selectById(orderId);
        if (Objects.isNull(order)) {
            throw new CustomerException("订单不存在");
        }
        if (order.getOrderStatus().equals(OrderStatusEnum.CANCEL.getCode())) {
            throw new CustomerException("订单已取消，请勿重复取消");
        }
        Integer oldStatus = order.getOrderStatus();
        order.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
        LambdaQueryWrapper<ImOrderDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ImOrderDetail::getOrderId, order.getId());
        List<ImOrderDetail> orderDetails = orderDetailMapper.selectList(queryWrapper);
        orderDetails.forEach(detail -> {
            updateStoreNum(order.getId(), detail.getProductId(), detail.getQuantity(), CANCEL_ORDER);
        });

        String productList = orderDetails.stream().sorted(Comparator.comparing(ImOrderDetail::getProductId)).map(s -> s.getProductId() + "&" + s.getQuantity()).collect(Collectors.joining(","));
        String encryptString = String.format("%s|%s", order.getConsigneeAddress(), productList);
        String md5 = MD5.create().digestHex(encryptString);
        redisUtil.hdel(TEMP_ORDER + md5, orderId);

        orderDetailMapper.delete(queryWrapper);
        orderMapper.updateById(order);

        if (oldStatus.equals(OrderStatusEnum.CREATED.getCode())) {
            String content = String.format("【%s】取消了订单【%s】", jwtUser.getRealName(), order.getOrderCode());
            noticeService.createNotice(NoticeTypeEnum.CANCEL_MESSAGE, content, order.getId(), sysUserService.getRoleUserMap(STORE_MANAGER.getCode()));
        }


        return true;
    }

    @Override
    public boolean lock(String orderId) {
        ImOrder order = orderMapper.selectById(orderId);
        if (Objects.isNull(order)) {
            throw new CustomerException("订单不存在");
        }
        if (order.getOrderStatus().equals(OrderStatusEnum.CANCEL.getCode()) || order.getOrderStatus().equals(OrderStatusEnum.DELIVER.getCode()) || order.getOrderStatus().equals(OrderStatusEnum.DELIVERED.getCode())) {
            throw new CustomerException("订单" + OrderStatusEnum.getInstance(order.getOrderStatus()).getName() + "，不允许锁单");
        }
//        if(order.getOrderStatus().equals(OrderStatusEnum.DELIVER.getCode())){
//            throw new CustomerException("订单已发货，不允许锁单");
//        }
//        if(order.getOrderStatus().equals(OrderStatusEnum.DELIVERED.getCode())){
//            throw new CustomerException("订单已签收，不允许锁单");
//        }
        if (order.getOrderStatus().equals(OrderStatusEnum.LOCKED.getCode())) {
            throw new CustomerException("订单已锁单，请勿重复锁单");
        }
        order.setOrderStatus(OrderStatusEnum.LOCKED.getCode());
        orderMapper.updateById(order);
        return true;
    }

    @Override
    public boolean unlock(String orderId) {
        ImOrder order = orderMapper.selectById(orderId);
        if (Objects.isNull(order)) {
            throw new CustomerException("订单不存在");
        }
        if (order.getOrderStatus().equals(OrderStatusEnum.CANCEL.getCode()) || order.getOrderStatus().equals(OrderStatusEnum.DELIVER.getCode()) || order.getOrderStatus().equals(OrderStatusEnum.DELIVERED.getCode())) {
            throw new CustomerException("订单" + OrderStatusEnum.getInstance(order.getOrderStatus()).getName() + "，不允许锁单");
        }
        order.setOrderStatus(OrderStatusEnum.CREATED.getCode());
        orderMapper.updateById(order);
        return true;
    }

    @Override
    public boolean deliver(String orderId) {
        ImOrder order = orderMapper.selectById(orderId);
        if (Objects.isNull(order)) {
            throw new CustomerException("订单不存在");
        }

        if (order.getOrderStatus().equals(OrderStatusEnum.CANCEL.getCode()) || order.getOrderStatus().equals(OrderStatusEnum.DELIVERED.getCode())) {
            throw new CustomerException("订单" + OrderStatusEnum.getInstance(order.getOrderStatus()).getName() + "，不允许发货");
        }
        if (order.getOrderStatus().equals(OrderStatusEnum.DELIVER.getCode())) {
            throw new CustomerException("订单已发货，请勿重复发货");
        }
        order.setOrderStatus(OrderStatusEnum.DELIVER.getCode());
        orderMapper.updateById(order);
        return true;
    }

    @Override
    public boolean unDeliver(String orderId) {
        ImOrder order = orderMapper.selectById(orderId);
        if (Objects.isNull(order)) {
            throw new CustomerException("订单不存在");
        }

        if (order.getOrderStatus().equals(OrderStatusEnum.CANCEL.getCode()) || order.getOrderStatus().equals(OrderStatusEnum.DELIVERED.getCode())) {
            throw new CustomerException("订单" + OrderStatusEnum.getInstance(order.getOrderStatus()).getName() + "，不允许发货");
        }
        order.setOrderStatus(OrderStatusEnum.CREATED.getCode());
        orderMapper.updateById(order);
        return true;
    }

    @Override
    public List<Long> getAllCreateOrderId() {
        LambdaQueryWrapper<ImOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ImOrder::getOrderStatus, OrderStatusEnum.CREATED.getCode());
        List<ImOrder> data = orderMapper.selectList(queryWrapper);
        List<Long> result = new ArrayList<>();
        if (!data.isEmpty()) {
            result = data.stream().map(ImOrder::getId).toList();
        }
        return result;
    }

    @Override
    public void exportCheckedOrder(ExportCheckedBo bo, HttpServletResponse response) throws IOException {
        List<String> targetIds = bo.getOrderIds();
        LambdaQueryWrapper<ImOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(ImOrder::getId, targetIds);
        List<ImOrder> orderList = orderMapper.selectList(queryWrapper);
        List<ProductSimpleVo> orderDetails = productMapper.listAllProductSimpleData(targetIds);
        Map<Long, List<ProductSimpleVo>> orderDetailMap = orderDetails.stream().collect(Collectors.groupingBy(ProductSimpleVo::getOrderId));
        List<ExportCreateDto> exportList = new ArrayList<>();
        orderList.forEach(order -> {
            ExportCreateDto dto = new ExportCreateDto();

            dto.setConsigneeName(order.getConsigneeName());
            dto.setConsigneeTel(order.getConsigneeTel());
            dto.setConsigneeAddress(order.getConsigneeAddress());
            dto.setSenderName(order.getSenderName());
            dto.setSenderTel(order.getSenderTel());
            dto.setSenderAddress(order.getSenderAddress());
            dto.setGoods("");
            List<ProductSimpleVo> products = orderDetailMap.get(order.getId());
            String remark = products.stream().map(s -> {
                StringBuffer sb = new StringBuffer();
                sb.append(s.getProductName());
                if (StringUtils.hasText(s.getLimitTime())) {
                    sb.append("（");
                    sb.append(s.getLimitTime());
                    sb.append("）");
                }
                sb.append("*");
                sb.append(s.getQuantity());
                return sb.toString();
            }).collect(Collectors.joining(String.valueOf((char) 10)));
            dto.setRemark(remark);


            exportList.add(dto);
        });

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = "123";
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), ExportCreateDto.class).sheet("面单信息").doWrite(exportList);

        if (bo.isShouldUpdate()) {
            LambdaUpdateWrapper<ImOrder> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.in(ImOrder::getId, bo.getOrderIds())
                    .set(ImOrder::getOrderStatus, OrderStatusEnum.DELIVER.getCode());
            orderMapper.update(updateWrapper);
        }

//        EasyExcelWriterFactory res = EasyExcelUtil.writeWithSheetsWeb(response,"待发货");
//        res.writeModel(ExportCreateDto.class, exportList,"sheetName2")
//                                 .finish();

//        String fileName = "F://" + System.currentTimeMillis() + ".xlsx";

        // 这里 需要指定写用哪个class去写，然后写到第一个sheet，名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
//        EasyExcel.write(fileName, ExportCreateDto.class)
//                .sheet("模板")
//                .doWrite(() -> {
//                    // 分页查询数据
//                   return exportList;
//                });
    }


    @Override
    public List<OrderLogVo> logByProduct(String productId, String orderStatus, String startTime, String endTime) {
        JwtUser jwtUser = authUtils.getUser();
        List<Long> allAllowUserList = new ArrayList<>();
        List<Long> storeUserList = sysUserService.getRoleUserMap("STORE_MANAGER");
        List<Long> adminUserList = sysUserService.getRoleUserMap("ADMIN");
        List<Long> superUserList = sysUserService.getRoleUserMap("SUPER_ADMIN");
        allAllowUserList.addAll(storeUserList);
        allAllowUserList.addAll(adminUserList);
        allAllowUserList.addAll(superUserList);
        String userId = null;
        if (!allAllowUserList.contains(jwtUser.getUserId())) {
            userId = jwtUser.getUserId().toString();
        }
        return orderMapper.logByProduct(productId, orderStatus, startTime, endTime, userId);

    }

    @Override
    public void exportLogByProduct(String productId, String orderStatus, String startTime, String endTime, HttpServletResponse response) throws IOException {
        List<OrderLogVo> data = logByProduct(productId, orderStatus, startTime, endTime);
        List<ExportOrderLogDto> record = data.stream().map(s -> {
            ExportOrderLogDto dto = new ExportOrderLogDto();
            BeanUtils.copyProperties(s, dto);
            dto.setOrderStatus(OrderStatusEnum.getInstance(s.getOrderStatus()).getName());
            dto.setAfloatStatus((s.getAfloatStatus() != null && s.getAfloatStatus() == 1) ? "是" : null);
            return dto;
        }).toList();

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = "123";
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), ExportOrderLogDto.class).sheet("订单信息").doWrite(record);

    }

    @Resource
    private ImProductLogMapper productLogMapper;

    @Transactional
    public void updateStoreNum(Long orderId, Long productId, Integer quantity, ProductLogOperationEnum operation) {
        if (quantity == null) {
            quantity = 0;
        }
        Object object = redisUtil.hget(WATCH_PRODUCT, productId.toString());
        if (object != null) {
            redisUtil.hset(WATCH_PRODUCT, productId.toString(), 2);
        }

        JwtUser jwtUser = authUtils.getUser();
        ImProduct product = productMapper.selectById(productId);
        Integer rawQuantity = product.getStoreNum();
        int resultQuantity;
        if (operation.equals(AFTER_EDIT_ORDER) || operation.equals(CREATE_ORDER)) {
            resultQuantity = rawQuantity - quantity;
            if (resultQuantity < 0) {
                throw new CustomerException("库存不足");
            }
        } else {
            resultQuantity = rawQuantity + quantity;
        }
        product.setStoreNum(resultQuantity);
        productMapper.updateById(product);

        ImProductLog productLog = new ImProductLog();
        productLog.setProductId(productId);
        productLog.setOperationType(operation.getType());
        productLog.setOperation(operation.getName());
        productLog.setOrderId(orderId);
        productLog.setQuantity(quantity);
        productLog.setStoreNum(resultQuantity);
        productLog.setOperationTime(LocalDateTime.now());
        productLog.setOperationUserId(jwtUser.getUserId());

        productLogMapper.insert(productLog);

    }


}
